package hierarchies;

import hips.Hierarchy;
import hips.Image;
import hips.Partition;
import hips.PixelValue;
import hips.Region;
import hips.gui.ComponentLocation;
import hips.gui.WaitingFrame;
import hips.region.NewRegionEvent;
import hips.region.NewRegionListener;
import hips.gui.parameters.ParameterFrame;
import hips.gui.parameters.ParameterListener;
import hips.images.floating.gray32.ImageGray32;
import hips.images.integer.gray16.ImageGray16;
import hips.images.integer.gray8.ImageGray8;
import hips.images.integer.rgb.ImageRGB;
import ij.ImageListener;
import ij.ImagePlus;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.LinkedList;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class Main implements ParameterListener, WindowListener, MouseListener, MouseMotionListener, NewRegionListener {

	static long ELAPSED = 50;
	Hierarchy h;
	MainFrame frame;
	long time;

	public hips.Image output;
	private Image f;
	private boolean listening;
	LinkedList<Long> times;

	ParameterFrame parameters;

	private Integer lastPixel;
	private Integer lastX;
	private Integer lastY;

	private Integer currentPartition;
	private ChangeListener sliderChange;

	public Main(Image f) {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception ex) {
		}

		this.f = f;

		WaitingFrame wait = new WaitingFrame();
		wait.setVisible(true);

		parameters = new ParameterFrame(f);
		parameters.iterationsLabel.setEnabled(false);
		parameters.iterationsSpinner.setEnabled(false);
		parameters.addParameterListener(this);

		output = f.newImage();
		h = new Hierarchy(f);
		h.addNewRegionEventListener(this);

		lastPixel = null;
		lastX = null;
		lastY = null;
		currentPartition = null;

		listening = false;
		times = new LinkedList<Long>();

		createInfoPanel();
		setSlider();
		setRadioButtons();

		ImagePlus.addImageListener(new ImageListener() {
			public void imageUpdated(ImagePlus arg0) {
			}

			public void imageOpened(ImagePlus arg0) {
			}

			public void imageClosed(ImagePlus arg0) {
				if (arg0 == h.getInput().getImagePlus()) {
					myDispose();
					ImagePlus.removeImageListener(this);
				}
			}
		});

		wait.dispose();
	}

	private void setSlider() {
		frame.slider.setMinimum(0);
		frame.slider.setMaximum(0);
		frame.slider.setMajorTickSpacing(0);
		frame.slider.setMinorTickSpacing(1);
		frame.slider.setValue(0);
		frame.slider.setSnapToTicks(true);
		frame.slider.setPaintTicks(true);
		frame.slider.setPaintLabels(true);
		frame.slider.setEnabled(false);
		sliderChange = new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				if (currentPartition == null || frame.slider.getValue() != currentPartition) {
					visualize((Integer) frame.slider.getValue());
				}
			}
		};
	}

	private void setRadioButtons() {
		frame.replaceLargest.setSelected(false);
		frame.replaceSmaller.setSelected(false);
		frame.replaceLargest.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (frame.replaceLargest.isSelected()) {
					frame.replaceSmaller.setSelected(false);
				}
			}
		});
		frame.replaceSmaller.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (frame.replaceSmaller.isSelected()) {
					frame.replaceLargest.setSelected(false);
				}
			}
		});
	}

	private void createInfoPanel() {
		frame = new MainFrame(this);
		frame.moreIterations.setEnabled(false);
		frame.pixels.setText("" + f.getSize());
		frame.channels.setText("" + f.getChannels());
		frame.slices.setText("" + f.getSlices());
		frame.dimensions.setText("" + f.getWidth() + " x " + f.getHeight());
		frame.title.setText(f.getTitle());
		frame.partitions.setText("0");
		frame.currentPartition.setText("-");
		if (f instanceof ImageGray8) {
			frame.type.setText("8-bit Gray");
		}
		if (f instanceof ImageGray16) {
			frame.type.setText("16-bit Gray");
		}
		if (f instanceof ImageGray32) {
			frame.type.setText("32-bit Gray (floating point)");
		}
		if (f instanceof ImageRGB) {
			frame.type.setText("24-bit RGB");
		}
		setPixelInfo();

		frame.addWindowListener(new WindowListener() {
			public void windowClosing(WindowEvent e) {
				if (e.getComponent() == frame) {
					frame.removeWindowListener(this);
					myDispose();
				}
			}

			public void windowOpened(WindowEvent e) {
			}

			public void windowIconified(WindowEvent e) {
			}

			public void windowDeiconified(WindowEvent e) {
			}

			public void windowDeactivated(WindowEvent e) {
			}

			public void windowClosed(WindowEvent e) {
			}

			public void windowActivated(WindowEvent e) {
			}
		});
	}

	public void removeListeners() {
		try {
			output.setRegionOfInterest(null);
			output.getImagePlus().getWindow().removeWindowListener(this);
			output.getImagePlus().getCanvas().removeMouseListener(this);
			output.getImagePlus().getCanvas().removeMouseMotionListener(this);
		} catch (NullPointerException e) {
		}
	}

	public void myDispose() {
		removeListeners();
		parameters.dispose();
		frame.dispose();
	}

	private void setPixelInfo() {
		if (lastPixel == null) {
			frame.valueSlice.setText("");
			frame.value.setText("");
			frame.meanValueSlice.setText("");
			frame.meanValueSlice2.setText("");
			frame.meanValue.setText("");
			frame.meanValue2.setText("");
			frame.pixel.setText("");
			frame.region.setText("");
			frame.regionSize.setText("");

			frame.belongsTo.setText("");
			frame.globalRangeSlice.setText("");
			frame.globalRange.setText("");
			frame.localRange.setText("");
			frame.cindex.setText("");
			frame.regions.setText("");
			frame.elapsed.setText("");
			frame.elapsed2.setText("");
			frame.method.setText("");
			frame.iterations.setText("");
			frame.random.setText("");
			return;
		}
		int slice = output.getImagePlus().getSlice() - 1;
		PixelValue pv = f.getPixelValue(lastPixel);
		frame.valueSlice.setText(pv.getString(slice));
		if (output.getSlices() > 1) {
			frame.value.setText(pv.getString());
		} else {
			frame.value.setText("-");
		}
		output.getPixelValue(pv, lastPixel);
		frame.meanValueSlice.setText(pv.getString(slice));
		frame.meanValueSlice2.setText(pv.getString(slice));
		if (output.getSlices() > 1) {
			frame.meanValue.setText(pv.getString());
			frame.meanValue2.setText(pv.getString());
		} else {
			frame.meanValue.setText("-");
			frame.meanValue2.setText("-");
		}
		frame.pixel.setText(xy(lastX, lastY));
		Region region = h.getRegion(lastPixel);
		frame.region.setText("" + region.getLabel());
		frame.regionSize.setText("" + region.getSize());

		int level = h.getLevelId(lastPixel);

		frame.belongsTo.setText("Partition " + level);

		Partition p = h.getPartition(level);
		frame.globalRangeSlice.setText(p.getOmega().getString(slice));
		if (output.getSlices() > 1) {
			frame.globalRange.setText(p.getOmega().getString());
		} else {
			frame.globalRange.setText("-");
		}
		frame.localRange.setText(formattedLocalRange(p.getAlpha().maxValue()));   // LINEA A CAMBIARRR
		frame.cindex.setText("" + p.getCindex());
		frame.regions.setText("" + p.getRegionSize());
		frame.elapsed.setText(formattedTime(times.get(level)));
		frame.elapsed2.setText(formattedTime(times.get(level)));
		frame.random.setText("" + p.getRandomPixels());
		frame.iterations.setText("1");
		if (p.getMethod() == Partition.IMPROVED_WITHOUT_CINDEX) {
			frame.method.setText("Improved, without c. index");
		}
		if (p.getMethod() == Partition.IMPROVED_WITH_CINDEX) {
			frame.method.setText("Improved, with c. index");
		}
		if (p.getMethod() == Partition.ORIGINAL) {
			frame.method.setText("Original");
		}
	}

	public static String formattedTime(long time) {
		int minutes = (int) (time / 60000);
		float seconds = (time / 1000f - minutes * 60);
		String stringTime = "";
		if (minutes > 0) {
			stringTime += minutes + " min  ";
		}
		return stringTime + new DecimalFormat("0.#").format(seconds).replace(",", ".") + " sec";
	}

	public static String xy(int x, int y) {
		return "(" + x + ",   " + y + ")";
	}

	public static String formattedLocalRange(Number alpha) {
		if (alpha instanceof Integer) {
			return alpha.toString();
		}
		return new DecimalFormat("0.0#").format(alpha).replace(",", ".");
	}

	public void visualize(int level) {
		h.setPartition(level);
		frame.currentPartition.setText("" + level);
		currentPartition = level;
	}

	private void listen() {
		if (!listening) {
			output.getImagePlus().getCanvas().addMouseListener(this);
			output.getImagePlus().getCanvas().addMouseMotionListener(this);
			output.getImagePlus().getWindow().addWindowListener(this);
			listening = true;
		}
	}

	private void dontListen() {
		if (listening) {
			output.getImagePlus().getCanvas().removeMouseListener(this);
			output.getImagePlus().getCanvas().removeMouseMotionListener(this);
			output.getImagePlus().getWindow().removeWindowListener(this);
			listening = false;
		}
	}

	public void createPartition() {
		frame.setEnabled(false);
		parameters.setVisible(true);
		dontListen();
	}

	public void close() {
		if (output.getImagePlus() != null) {
			output.getImagePlus().getCanvas().removeMouseListener(this);
			output.getImagePlus().getCanvas().removeMouseMotionListener(this);
			output.getImagePlus().getWindow().removeWindowListener(this);
			output.getImagePlus().close();
		}
	}

	public void mouseClicked(MouseEvent e) {
		if (e.getButton() != MouseEvent.BUTTON1) {
			return;
		}
		int x = output.getImagePlus().getCanvas().offScreenX(e.getX());
		int y = output.getImagePlus().getCanvas().offScreenY(e.getY());
		int pos = output.getWidth() * y + x;

		boolean changed = false;
		if (frame.replaceLargest.isSelected()) {
			changed = h.increaseLevel(pos);
		} else if (frame.replaceSmaller.isSelected()) {
			changed = h.decreaseLevel(pos);
		}
		if (changed == true) {
			currentPartition = null;
			frame.currentPartition.setText("Custom");
			setPixelInfo();
		}
		Region r = h.getRegion(pos);
		output.setRegionOfInterest(r);
	}

	public void mouseMoved(MouseEvent e) {
		int x = output.getImagePlus().getCanvas().offScreenX(e.getX());
		int y = output.getImagePlus().getCanvas().offScreenY(e.getY());
		int pos = output.getWidth() * y + x;
		Region r = h.getRegion(pos);
		output.setRegionOfInterest(r);
		if ((new Date()).getTime() - time > ELAPSED) {
			lastPixel = pos;
			lastX = x;
			lastY = y;
			setPixelInfo();
			time = System.currentTimeMillis();
		}
	}

	public void mouseExited(MouseEvent e) {
		lastPixel = null;
		lastX = null;
		lastY = null;
		setPixelInfo();
		output.setRegionOfInterest(null);
	}

	public void windowClosed(WindowEvent e) {
		if (e.getWindow() == output.getImagePlus().getWindow()) {
			// info.dispose();
		}
	}

	public void newRegionCreated(NewRegionEvent e) {
		Region r = e.getRegion();
		output.paintRegion(r, f.getMeanValue(r));
		output.getImagePlus().updateAndDraw();
	}

	public void ParametersOk(int type, final PixelValue alpha, final PixelValue omega, final float cindex, final int randomPixels, int iterations) {
		Thread t = new Thread(new Runnable() {
			public void run() {
				Integer place = h.getPlaceForPartition(alpha, omega, cindex, randomPixels);
				if (place == null) {
					// errorLabel.setText("partition couldn't be created");
					frame.setEnabled(true);
					return;
				}
				long t = System.currentTimeMillis();
				if (output.getImagePlus().getWindow() == null) {
					output.getImagePlus().show();
					ComponentLocation.setLocationCenter(0.75f, 0.5f, output.getImagePlus().getWindow());
				}
				output.clear();
				h.addPartition(alpha, omega, cindex, randomPixels);
				t = System.currentTimeMillis() - t;
				times.add(place, t);
				// frame.setPartitions(h.getSize());
				h.setPartition(place);
				currentPartition = place;

				frame.slider.removeChangeListener(sliderChange);
				frame.slider.setMaximum(h.getSize() - 1);
				if (h.getSize() > 1) {
					frame.slider.setEnabled(true);
					frame.slider.setValue(place);
					frame.slider.addChangeListener(sliderChange);
				}
				frame.currentPartition.setText("" + place);
				frame.partitions.setText("" + h.getSize());
				frame.setEnabled(true);
				listen();
			}
		});
		t.start();
	}

	public void ParametersCancelled() {
		frame.setEnabled(true);
	}

	public void mousePressed(MouseEvent e) {
	}

	public void mouseReleased(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseDragged(MouseEvent e) {
	}

	public void windowOpened(WindowEvent e) {
	}

	public void windowClosing(WindowEvent e) {
	}

	public void windowIconified(WindowEvent e) {
	}

	public void windowDeiconified(WindowEvent e) {
	}

	public void windowActivated(WindowEvent e) {
	}

	public void windowDeactivated(WindowEvent e) {
	}
}
